﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace zanpen2000.common
{
    /*
   * 快速计算任意大小的文件的MD5，调用方法如下：
   * 
          string fp = @"d:\databases\Dremis_LandTax_fgj_1.ldf";
          string hexStr = HashCompute.HashFileByMd5(fp);
          Console.WriteLine(hexStr);
          Console.ReadKey();

   */

    /// <summary>
    /// 给定文件, 根据策略计算并生成文件的Md5， 以16进制字符串的形式返回
    /// Author：张鹏
    /// Date：2013/5/31
    /// </summary>
    public class QuickHashComputer: IDisposable
    {
       string FileName;

        /// <summary>
        /// 拆分份数
        /// </summary>
        int BreakNumber = HashVariables.BreakNumber;

        /// <summary>
        /// 达到需要拆分的大小(字节),
        /// </summary>
        public long EnoughToBreakup = HashVariables.EnoughToBreakup;

        /// <summary>
        /// 每份length
        /// </summary>
        int EachLength = HashVariables.EachLength;

        MD5 M = MD5.Create();
        FileInfo Info;
        List<byte[]> listBytes = new List<byte[]>();

        /// <summary>
        /// 以16进制字符串返回指定文件的md5
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <returns></returns>
        public static string HashingFile(string fileName)
        {
            using (QuickHashComputer fm = new QuickHashComputer(fileName))
            {
                try
                {
                    return fm.Get();
                }
                catch (Exception)
                {
                    return "";
                }
                
            }
        }

        ///<summary>
        /// MD5 算法的哈希值大小为 128 位。  
        /// </summary>  
        /// <param name="s">哈希前的字符串</param>  
        /// <returns>哈希后的字符串</returns>  
        public static string HashString(string s)
        {
            // 将字符串编码为一个字节序列  
            byte[] bufferValue = Encoding.UTF8.GetBytes(s);
            // 定义加密哈希算法操作类，在System.Security.Cryptography 命名空间 下  
            HashAlgorithm ha = new MD5CryptoServiceProvider();
            // 计算指定字节数组的哈希值  
            byte[] bufferHash = ha.ComputeHash(bufferValue);
            // 释放由 HashAlgorithm 类使用的所有资源  
            ha.Clear();
            // 将 8 位无符号整数数组的值转换为它的等效 String 表示形式（使用 base 64 数字编码）。  
            return Convert.ToBase64String(bufferHash);
        }

        /// <summary>
        /// 构造方法（私有）
        /// </summary>
        /// <param name="fileName"></param>
        private QuickHashComputer(string fileName)
        {
            if (File.Exists(fileName))
            {
                this.FileName = fileName;
                Info = new FileInfo(this.FileName);
                M.Initialize();
            }
            //else
            //    throw new FileNotFoundException();
        }

        /// <summary>
        /// 返回计算好的Md5值
        /// </summary>
        /// <returns></returns>
        private string Get()
        {
            FileStream fs = new FileStream(this.FileName, FileMode.Open, FileAccess.Read);

            byte[] hashBytes;
            try
            {
                if (__needBreakup())
                {
                    long seag = Info.Length / BreakNumber;
                    if (fs.CanSeek)
                    {
                        for (int i = 0; i < listBytes.Count; i++)
                        {
                            fs.Seek(i * seag, SeekOrigin.Begin);
                            fs.Read(listBytes[i], 0, EachLength);

                            M.ComputeHash(listBytes[i]);
                        }
                        hashBytes = M.Hash;
                    }
                    else throw new IOException("文件内指针定位失败!");
                }
                else
                {
                    hashBytes = M.ComputeHash(fs);
                }
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                fs.Close();
            }
            return __byteToHexStr(hashBytes);
        }

        /// <summary>
        /// 是否需要分割
        /// </summary>
        /// <returns></returns>
        bool __needBreakup()
        {
            listBytes.Clear();
            if (Info.Length >= EnoughToBreakup)
            {
                //初始化字节数组
                for (int i = 0; i < BreakNumber; i++)
                {
                    listBytes.Add(new byte[EachLength]);
                }
                return true;
            }
            else return false;
        }

        /// <summary>
        /// 字节数组转为16进制字符串
        /// </summary>
        /// <param name="md5"></param>
        /// <returns></returns>
        string __byteToHexStr(byte[] md5)
        {
            StringBuilder sb = new StringBuilder(32);
            for (int i = 0; i < md5.Length; i++)
            {
                sb.Append(md5[i].ToString("X2"));
            }
            return sb.ToString();
        }

        public void Dispose()
        {
            if (M != null)
            {
                M.Clear();
            }
        }
    }

    public class HashVariables
    {
        /// <summary>
        /// 拆分份数
        /// </summary>
        public static int BreakNumber = 5;

        /// <summary>
        /// 达到需要拆分的大小(字节),
        /// </summary>
        public static long EnoughToBreakup = 4096 * 5;

        /// <summary>
        /// 每个切片的长度
        /// </summary>
        public static int EachLength = 4096;
    }
}
